<?php
declare(strict_types=1);

namespace app\service;

use app\model\{Order, User, Coupon, UserBalanceFlow, Merchant, MerchantFundFlow};
use app\service\Kuaidi100Service;
use think\facade\{Db, Log};
use think\Exception;

class OrderService extends BaseService
{
    /**
     * 创建订单
     */
    public function create(array $data): Order
    {
        Db::startTrans();
        try {
            // 如果未传预约时间,设置为最近可选时间
            if (empty($data['pickup_date']) || empty($data['pickup_start_time'])) {
                $defaultTime = $this->getDefaultPickupTime();
                $data['pickup_date'] = $defaultTime['date'];
                $data['pickup_start_time'] = $defaultTime['start_time'];
            }

            // 验证预约时间
            $this->validatePickupTime($data['pickup_date'], $data['pickup_start_time']);

            // 生成订单号
            $data['order_no'] = $this->generateOrderNo();
            
            // 计算运费
            $expressService = new ExpressService();
            $priceInfo = $expressService->calculatePrice(
                $data['company_code'],
                $data['sender_province'],
                $data['sender_city'],
                $data['receiver_province'], 
                $data['receiver_city'],
                $data['weight_estimate']
            );
            
            // 计算保价费
            $insureFee = 0;
            if (!empty($data['insure_amount']) && $data['insure_amount'] > 0) {
                $insureFee = $expressService->calculateInsureFee($data['company_code'], $data['insure_amount']);
            }
            
            // 计算优惠金额
            $couponAmount = 0;
            if (!empty($data['coupon_id'])) {
                $coupon = Coupon::find($data['coupon_id']);
                if ($coupon && $coupon->checkAvailable($priceInfo['total_fee'])) {
                    $couponAmount = $coupon->calculateDiscount($priceInfo['total_fee']);
                }
            }
            
            // 计算实际支付金额
            $feeActual = $priceInfo['total_fee'] + $insureFee - $couponAmount;
            
            // 创建订单
            $order = Order::create([
                'merchant_id' => request()->merchant_id,
                'user_id' => request()->user_id,
                'order_no' => $data['order_no'],
                'company_code' => $data['company_code'],
                'business_type' => $data['business_type'],
                'sender_name' => $data['sender_name'],
                'sender_phone' => $data['sender_phone'],
                'sender_province' => $data['sender_province'],
                'sender_city' => $data['sender_city'],
                'sender_county' => $data['sender_county'],
                'sender_address' => $data['sender_address'],
                'receiver_name' => $data['receiver_name'],
                'receiver_phone' => $data['receiver_phone'],
                'receiver_province' => $data['receiver_province'],
                'receiver_city' => $data['receiver_city'],
                'receiver_county' => $data['receiver_county'],
                'receiver_address' => $data['receiver_address'],
                'weight_estimate' => $data['weight_estimate'],
                'fee_estimate' => $priceInfo['total_fee'],
                'insure_amount' => $data['insure_amount'] ?? 0,
                'insure_fee' => $insureFee,
                'coupon_id' => $data['coupon_id'] ?? 0,
                'coupon_amount' => $couponAmount,
                'fee_actual' => $feeActual,
                'remark' => $data['remark'] ?? '',
                'order_status' => 0, // 待支付
                'merchant_profit' => $priceInfo['merchant_profit'],
                'platform_profit' => $priceInfo['platform_profit'],
                'pickup_date' => $data['pickup_date'],
                'pickup_start_time' => $data['pickup_start_time'],
                'pickup_end_time' => date('H:i', strtotime($data['pickup_start_time']) + 2*3600)
            ]);

            // 使用优惠券
            if (!empty($data['coupon_id'])) {
                $coupon->use($order->id);
            }

            Db::commit();
            return $order;

        } catch (\Exception $e) {
            Db::rollback();
            Log::error('创建订单失败: ' . $e->getMessage());
            throw new Exception('创建订单失败: ' . $e->getMessage());
        }
    }

    /**
     * 取消订单
     */
    public function cancel(string $orderNo, int $userId): bool
    {
        Db::startTrans();
        try {
            $order = Order::where([
                'order_no' => $orderNo,
                'user_id' => $userId
            ])->find();

            if (!$order) {
                throw new Exception('订单不存在');
            }

            // 检查订单状态
            // 0:未支付 1:已支付待取件 2:已取件 9:用户取消 10:已取件 11:揽货失败 12:已退回 13:已签收 14:异常签收 15:已结算 99:订单已取消
            if (!in_array($order->order_status, [0, 1])) { 
                throw new Exception('只有未支付或待取件的订单可以取消');
            }

            // 如果已支付则退款
            if ($order->order_status == 1) {
                // 退还用户支付金额
                $this->refund($order);

                // 退还商家成本
                $this->refundMerchantCost($order);
            }

            // 更新订单状态
            $order->order_status = 9; // 用户取消
            $order->save();

            // 退还优惠券
            if ($order->coupon_id > 0) {
                Coupon::where('id', $order->coupon_id)->update(['status' => 0]);
            }

            // 如果是快递100订单,需要调用快递100取消接口
            if ($order->kuaidi100_order_id) {
                $kuaidi100Service = new Kuaidi100Service();
                $kuaidi100Service->cancelOrder($order->kuaidi100_order_id);
            }

            Db::commit();
            return true;

        } catch (\Exception $e) {
            Db::rollback();
            Log::error('取消订单失败: ' . $e->getMessage());
            throw new Exception('取消订单失败: ' . $e->getMessage());
        }
    }

    /**
     * 余额支付
     */
    public function balancePay(Order $order): array
    {
        Db::startTrans();
        try {
            $user = User::find($order->user_id);
            if ($user->balance < $order->fee_actual) {
                throw new Exception('余额不足');
            }

            // 扣除用户余额
            $user->balance -= $order->fee_actual;
            $user->save();

            // 记录余额流水
            UserBalanceFlow::create([
                'merchant_id' => $order->merchant_id,
                'user_id' => $user->id,
                'order_no' => $order->order_no,
                'type' => 3, // 订单支付
                'balance_change' => -$order->fee_actual,
                'balance_before' => $user->balance + $order->fee_actual,
                'balance_after' => $user->balance
            ]);

            // 更新订单状态
            $order->order_status = 1; // 待揽收
            $order->save();

            Db::commit();
            return ['success' => true];

        } catch (\Exception $e) {
            Db::rollback();
            Log::error('余额支付失败: ' . $e->getMessage());
            throw new Exception('余额支付失败: ' . $e->getMessage());
        }
    }

    /**
     * 获取订单
     */
    public function getOrder(string $orderNo, int $userId): Order
    {
        $order = Order::where([
            'order_no' => $orderNo,
            'user_id' => $userId
        ])->find();

        if (!$order) {
            throw new Exception('订单不存在');
        }

        return $order;
    }

    /**
     * 获取用户订单列表
     */
    public function getUserOrders(int $userId, array $params = []): array
    {
        $where = ['user_id' => $userId];
        
        // 订单状态筛选
        if (isset($params['status']) && $params['status'] !== '') {
            $where['order_status'] = $params['status'];
        }
        
        $page = $params['page'] ?? 1;
        $limit = $params['limit'] ?? 10;

        $list = Order::where($where)
            ->with(['expressCompany'])
            ->order('id', 'desc')
            ->page($page, $limit)
            ->select();

        $total = Order::where($where)->count();

        return [
            'list' => $list,
            'total' => $total,
            'page' => $page,
            'limit' => $limit
        ];
    }

    /**
     * 获取订单详情
     */
    public function getOrderDetail(string $orderNo, int $userId): Order
    {
        $order = Order::where([
            'order_no' => $orderNo,
            'user_id' => $userId
        ])
        ->with(['expressCompany', 'kuaidi100Order'])
        ->find();

        if (!$order) {
            throw new Exception('订单不存在');
        }

        return $order;
    }

    /**
     * 生成订单号
     */
    protected function generateOrderNo(): string
    {
        return date('YmdHis') . mt_rand(1000000, 9999999);
    }

    /**
     * 退款
     */
    protected function refund(Order $order): bool
    {
        try {
            switch ($order->payment_method) {
                case 1: // 微信支付
                    $wechatService = new WechatService($order->merchant_id);
                    return $wechatService->refund($order);
                case 2: // 支付宝支付
                    $alipayService = new AlipayService($order->merchant_id);
                    return $alipayService->refund($order);
                case 3: // 余额支付
                    return $this->balanceRefund($order);
                default:
                    throw new Exception('不支持的支付方式');
            }
        } catch (\Exception $e) {
            Log::error('退款失败: ' . $e->getMessage());
            throw new Exception('退款失败: ' . $e->getMessage());
        }
    }

    /**
     * 余额退款
     */
    protected function balanceRefund(Order $order): bool
    {
        Db::startTrans();
        try {
            $user = User::find($order->user_id);
            
            // 增加用户余额
            $user->balance += $order->fee_actual;
            $user->save();

            // 记录余额流水
            UserBalanceFlow::create([
                'merchant_id' => $order->merchant_id,
                'user_id' => $user->id,
                'order_no' => $order->order_no,
                'type' => 4, // 订单退款
                'balance_change' => $order->fee_actual,
                'balance_before' => $user->balance - $order->fee_actual,
                'balance_after' => $user->balance
            ]);

            Db::commit();
            return true;

        } catch (\Exception $e) {
            Db::rollback();
            Log::error('余额退款失败: ' . $e->getMessage());
            throw new Exception('余额退款失败: ' . $e->getMessage());
        }
    }

    /**
     * 退还商家成本
     */
    protected function refundMerchantCost(Order $order): bool
    {
        try {
            // 计算商家成本 = 实际运费 - 商家利润 - 平台利润
            $merchantCost = $order->fee_actual - $order->merchant_profit - $order->platform_profit;
            
            if ($merchantCost <= 0) {
                return true;
            }

            // 增加商家余额
            $merchant = Merchant::find($order->merchant_id);
            $merchant->balance += $merchantCost;
            $merchant->save();

            // 记录商家资金流水
            MerchantFundFlow::create([
                'merchant_id' => $order->merchant_id,
                'fund_flow_type' => 4, // 订单退款
                'order_no' => $order->order_no,
                'fund_flow_amount' => $merchantCost,
                'fund_flow_time' => date('Y-m-d H:i:s')
            ]);

            return true;

        } catch (\Exception $e) {
            Log::error('退还商家成本失败: ' . $e->getMessage());
            throw new Exception('退还商家成本失败: ' . $e->getMessage());
        }
    }

    /**
     * 处理订单支付成功
     */
    public function handlePaySuccess(Order $order, array $payData): bool
    {
        try {
            // 检查订单状态
            if ($order->order_status != 0) {
                throw new Exception('订单状态异常');
            }

            // 更新订单信息
            $order->save([
                'order_status' => 1, // 已支付待取件
                'payment_method' => $payData['payment_method'],
                'payment_time' => $payData['pay_time'],
                'transaction_id' => $payData['transaction_id']
            ]);

            // 记录商家资金流水
            MerchantFundFlow::create([
                'merchant_id' => $order->merchant_id,
                'fund_flow_type' => 3, // 订单支付
                'order_no' => $order->order_no,
                'fund_flow_amount' => $order->fee_actual,
                'fund_flow_time' => $payData['pay_time']
            ]);

            // 如果是余额支付，记录用户余额流水
            if ($payData['payment_method'] == 3) {
                UserBalanceFlow::create([
                    'merchant_id' => $order->merchant_id,
                    'user_id' => $order->user_id,
                    'order_no' => $order->order_no,
                    'type' => 3, // 订单支付
                    'balance_change' => -$order->fee_actual,
                    'balance_before' => $payData['balance_before'],
                    'balance_after' => $payData['balance_after']
                ]);
            }

            // 调用快递100下单
            $kuaidi100Service = new Kuaidi100Service();
            $result = $kuaidi100Service->createOrder($order);
            if (!$result) {
                throw new Exception('快递100下单失败');
            }

            return true;

        } catch (\Exception $e) {
            Log::error('处理订单支付失败: ' . $e->getMessage());
            throw new Exception('处理订单支付失败: ' . $e->getMessage());
        }
    }

    /**
     * 处理订单退款
     */
    public function handleRefund(Order $order): bool
    {
        try {
            // 检查订单状态
            if (!in_array($order->order_status, [1, 2])) { // 已支付待取件或待揽收
                throw new Exception('订单状态不允许退款');
            }

            // 根据支付方式调用对应的退款服务
            switch ($order->payment_method) {
                case 1: // 微信支付
                    $wechatService = new WechatService($order->merchant_id);
                    $result = $wechatService->refund($order);
                    break;
                    
                case 2: // 支付宝支付
                    $alipayService = new AlipayService($order->merchant_id);
                    $result = $alipayService->refund($order);
                    break;
                    
                case 3: // 余额支付
                    $result = $this->handleBalanceRefund($order);
                    break;
                    
                default:
                    throw new Exception('不支持的支付方式');
            }

            if (!$result) {
                throw new Exception('退款申请失败');
            }

            // 更新订单状态
            $order->save([
                'order_status' => 99, // 已取消
                'cancel_time' => date('Y-m-d H:i:s'),
                'cancel_reason' => '用户申请退款'
            ]);

            // 记录商家资金流水
            MerchantFundFlow::create([
                'merchant_id' => $order->merchant_id,
                'fund_flow_type' => 4, // 订单退款
                'order_no' => $order->order_no,
                'fund_flow_amount' => -$order->fee_actual,
                'fund_flow_time' => date('Y-m-d H:i:s')
            ]);

            return true;

        } catch (\Exception $e) {
            Log::error('处理订单退款失败: ' . $e->getMessage());
            throw new Exception('处理订单退款失败: ' . $e->getMessage());
        }
    }

    /**
     * 处理余额支付退款
     */
    protected function handleBalanceRefund(Order $order): bool
    {
        // 获取用户信息
        $user = User::find($order->user_id);
        if (!$user) {
            throw new Exception('用户不存在');
        }

        // 计算退款前后余额
        $balanceBefore = $user->balance;
        $balanceAfter = $balanceBefore + $order->fee_actual;

        // 更新用户余额
        $user->save(['balance' => $balanceAfter]);

        // 记录用户余额流水
        UserBalanceFlow::create([
            'merchant_id' => $order->merchant_id,
            'user_id' => $order->user_id,
            'order_no' => $order->order_no,
            'type' => 4, // 订单退款
            'balance_change' => $order->fee_actual,
            'balance_before' => $balanceBefore,
            'balance_after' => $balanceAfter
        ]);

        return true;
    }

    /**
     * 获取可预约时间段列表
     */
    public function getPickupTimeSlots(): array
    {
        $slots = [];
        $startHour = 10; // 早上10点开始
        $endHour = 19;   // 晚上19点结束
        $interval = 2;    // 2小时一个时间段
        
        // 获取今天到后天的日期
        $dates = [];
        for ($i = 0; $i <= 2; $i++) {
            $dates[] = date('Y-m-d', strtotime("+{$i} day"));
        }

        // 当前时间
        $now = time();
        
        foreach ($dates as $date) {
            $daySlots = [];
            $timestamp = strtotime($date);
            
            // 遍历时间段
            for ($hour = $startHour; $hour < $endHour; $hour += $interval) {
                $startTime = $hour . ':00';
                $endTime = ($hour + $interval) . ':00';
                
                // 时间段开始时间戳
                $slotTimestamp = strtotime($date . ' ' . $startTime);
                
                // 如果是今天,且时间段已过,则跳过
                if (date('Y-m-d') == $date && $slotTimestamp <= $now) {
                    continue;
                }
                
                $daySlots[] = [
                    'start_time' => $startTime,
                    'end_time' => $endTime,
                    'text' => $startTime . '-' . $endTime
                ];
            }
            
            if (!empty($daySlots)) {
                $slots[] = [
                    'date' => $date,
                    'text' => $this->formatDateText($date),
                    'time_slots' => $daySlots
                ];
            }
        }
        
        return $slots;
    }

    /**
     * 格式化日期显示文本
     */
    protected function formatDateText(string $date): string
    {
        $timestamp = strtotime($date);
        $today = strtotime(date('Y-m-d'));
        
        if ($timestamp == $today) {
            return '今天';
        } elseif ($timestamp == strtotime('+1 day', $today)) {
            return '明天';
        } elseif ($timestamp == strtotime('+2 day', $today)) {
            return '后天';
        }
        
        return date('m月d日', $timestamp);
    }

    /**
     * 验证预约时间是否有效
     */
    public function validatePickupTime(string $date, string $startTime): bool
    {
        // 验证日期范围
        $timestamp = strtotime($date);
        $today = strtotime(date('Y-m-d'));
        $maxDate = strtotime('+2 day', $today);
        
        if ($timestamp < $today || $timestamp > $maxDate) {
            throw new Exception('预约日期超出可选范围');
        }
        
        // 验证时间段
        $hour = (int)substr($startTime, 0, 2);
        if ($hour < 10 || $hour > 17 || $hour % 2 != 0) {
            throw new Exception('预约时间段无效');
        }
        
        // 如果是今天,检查时间是否已过
        if ($date == date('Y-m-d')) {
            $slotTimestamp = strtotime($date . ' ' . $startTime);
            if ($slotTimestamp <= time()) {
                throw new Exception('该时间段已过');
            }
        }
        
        return true;
    }

    /**
     * 获取默认预约时间(最近可选时间)
     */
    protected function getDefaultPickupTime(): array
    {
        $now = time();
        $today = date('Y-m-d');
        $currentHour = (int)date('H');
        
        // 获取今天剩余的时间段
        $startHour = 10; // 早上10点开始
        $endHour = 19;   // 晚上19点结束
        $interval = 2;    // 2小时一个时间段

        // 计算下一个可用时间段
        $nextHour = $currentHour + (2 - ($currentHour % 2)); // 向上取整到最近的双数小时
        
        // 如果当前时间+1小时超过了今天的最后时间段,则取明天第一个时间段
        if ($nextHour >= $endHour) {
            return [
                'date' => date('Y-m-d', strtotime('+1 day')),
                'start_time' => sprintf('%02d:00', $startHour)
            ];
        }
        
        // 如果下一个时间段在今天的营业时间内
        if ($nextHour >= $startHour && $nextHour < $endHour) {
            return [
                'date' => $today,
                'start_time' => sprintf('%02d:00', $nextHour)
            ];
        }
        
        // 如果当前时间早于营业时间,则取今天第一个时间段
        if ($currentHour < $startHour) {
            return [
                'date' => $today,
                'start_time' => sprintf('%02d:00', $startHour)
            ];
        }
        
        // 其他情况取明天第一个时间段
        return [
            'date' => date('Y-m-d', strtotime('+1 day')),
            'start_time' => sprintf('%02d:00', $startHour)
        ];
    }
} 